﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Windows.Forms;

namespace AutoUpgrade
{
    internal partial class UpgradeForm : AutoUpgrade.SkinForm
    {
        /// <summary>
        /// 复制失败的文件
        /// </summary>
        private readonly Dictionary<string, string> copy_fall_file_list = new Dictionary<string, string>();
        private Point DownPoint; // 窗口拖动位置
        private Rectangle closeRectangle;
        private readonly System.Net.WebClient webClient;
        private readonly Font Title_Font = new Font("微软雅黑", 9.0f);
        private readonly List<VersionInfo> Delete_List = new List<VersionInfo>();
        private readonly List<VersionInfo> Download_List = new List<VersionInfo>();
        private readonly Dictionary<int, long> Up_Size = new Dictionary<int, long>();
        private bool Allow_Colse = false; // 是否允许关闭窗口
        internal long SumSize = 0;
        private int Index = 0;
        private int Retry_Index = 0;
        private Exception exception;

        /// <summary>
        /// 更新文件夹的缓存目录
        /// </summary>
        private readonly string updaterDirectory;

        /// <summary>
        /// 构建 带阴影的无边框窗口 实例。
        /// </summary>
        public UpgradeForm()
        {
            // 此调用是设计器所必需的。
            InitializeComponent();

            // 在 InitializeComponent() 调用之后添加任何初始化。
            this.webClient = new System.Net.WebClient();
            this.Icon = Properties.Resources.icon;
            this.webClient.DownloadFileCompleted += this.WebClient_DownloadFileCompleted;
            this.webClient.DownloadProgressChanged += this.WebClient_DownloadProgressChanged;

            string CurrentDirectory = Environment.CurrentDirectory;
            updaterDirectory = System.IO.Path.Combine(Application.StartupPath, "Updater");
            string tmp_png = System.IO.Path.Combine(CurrentDirectory, "Updater.png");
            string tmp_jpg = System.IO.Path.Combine(CurrentDirectory, "Updater.jpg");
            string tmp_gif = System.IO.Path.Combine(CurrentDirectory, "Updater.gif");
            string path = string.Empty;
            if (System.IO.File.Exists(tmp_png))
                path = tmp_png;
            else if (System.IO.File.Exists(tmp_jpg))
                path = tmp_jpg;
            else if (System.IO.File.Exists(tmp_gif))
                path = tmp_gif;
            if (!string.IsNullOrEmpty(path))
            {
                string fileName = Path.Combine(Application.LocalUserAppDataPath, new FileInfo(path).Name);
                File.Copy(path, fileName, true);
                this.PictureBox1.Load(fileName);
                var img = this.PictureBox1.Image;
                this.PictureBox1.Size = img.Size;
                this.Width = img.Width;
                this.Height = img.Height + 48;
            }
        }

        private void WebClient_DownloadProgressChanged(object sender, System.Net.DownloadProgressChangedEventArgs e)
        {
            if (this.Up_Size.ContainsKey(this.Index) == false)
                this.Up_Size.Add(this.Index, e.TotalBytesToReceive);
            List<long> list = new List<long>();
            foreach (long v in this.Up_Size.Values) // 显示所有值
                list.Add(v);
            long Sum = 0;
            if (list.Count > 1)
            {
                for (int i = 0; i <= list.Count - 2; i++)
                    Sum += list[i];
            }
            if (this.SumSize > 0)
            {
                double value = (e.BytesReceived + Sum) / (double)this.SumSize;
                value = Math.Min(value, 1);
                this.progress1.Value = (float)value;
                this.Label2.Text = value.ToString("P");
            }
        }

        private void WebClient_DownloadFileCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
        {
            DownloadNextFile();
        }


        private void DownloadNextFile()
        {
            if (this.Index < (this.Download_List.Count))
            {
                try
                {

                    if (!System.IO.Directory.Exists(updaterDirectory))
                        System.IO.Directory.CreateDirectory(updaterDirectory);
                    VersionInfo Info = Download_List[this.Index];
                    string destPath = System.IO.Path.Combine(updaterDirectory, Info.Name);
                    this.Label1.Text = "正在下载:" + Info.Name + "...";

                    Uri Uri = null;
                    if (System.Uri.TryCreate(Info.Uri + GlobalSetting.Suffix, UriKind.Absolute, out Uri) == false)
                        Uri = new Uri(GlobalSetting.Host + Info.Name + GlobalSetting.Suffix);
                    FileInfo FileInfo = new FileInfo(destPath);
                    if (!System.IO.Directory.Exists(FileInfo.Directory.FullName))
                        System.IO.Directory.CreateDirectory(FileInfo.Directory.FullName);
                    webClient.DownloadFileAsync(Uri, destPath);
                    this.Index += 1;
                }
                catch (Exception ex)
                {
                    Retry_Index += 1;
                    if (Retry_Index > 3)
                    {
                        MessageBox.Show("下载文件发生错误，更新失败。错误原因： " + ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, MessageBoxOptions.ServiceNotification);
                        this.exception = ex;
                        this.Close();
                    }
                    else
                    {
                        this.Label1.Text = string.Format("下载失败,准备第{0}次重试...", Retry_Index);
                        this.DownloadNextFile();
                    }
                }
            }
            else
                this.BackgroundWorker1.RunWorkerAsync();
        }


        private void LoadUpdateFile()
        {
            // 查找客户端需要更新的文件和需要删除的文件。
            if (GlobalSetting.LocalConfig != null)
            {
                foreach (VersionInfo oldVersion in GlobalSetting.LocalConfig.FileList)
                {
                    VersionInfo version = GlobalSetting.ServerConfig[oldVersion.Name];
                    if (version != null)
                    {
                        if (version.Version > oldVersion.Version || version.Md5 != oldVersion.Md5)
                            Download_List.Add(oldVersion);
                    }
                    else
                        Delete_List.Add(oldVersion);
                }
            }

            // 查找服务器端新增需要下载的文件。
            if (GlobalSetting.ServerConfig.FileList != null)
            {
                foreach (VersionInfo newVersion in GlobalSetting.ServerConfig.FileList)
                {
                    if (GlobalSetting.LocalConfig == null || GlobalSetting.LocalConfig.ContainsKey(newVersion.Name) == false)
                        Download_List.Add(newVersion);
                }
            }
            long SumSize = 0;
            foreach (VersionInfo Item in Download_List)
                SumSize += Item.Size;
            this.SumSize = SumSize;
        }

        private void UpgradeForm_Load(object sender, EventArgs e)
        {
            this.Timer1.Enabled = true;
        }

        private void Timer1_Tick(object sender, EventArgs e)
        {
            Action action = () =>
            {
                this.Timer1.Enabled = false;
                this.Label1.Text = "正在获取版本信息...";
                Application.DoEvents();

                var client = new UpgradeClient();
                client.Host = GlobalSetting.Host;
                client.StartupApplication = GlobalSetting.StartupApplication;
                if (client.CheckUpgrade())
                {
                    if (GlobalSetting.ServerConfig.Description != "")
                    {
                        this.Text = GlobalSetting.ServerConfig.Description;
                        this.PictureBox1.Invalidate();
                    }
                    this.Label1.Text = "正在获取更新信息...";
                    Application.DoEvents();
                    LoadUpdateFile();
                    DownloadNextFile();
                }
                else
                {
                    // 无需更新，退出
                    this.Label1.Text = "已是最新版本...";
                    Application.DoEvents();
                    System.Threading.Thread.Sleep(500);
                    this.Allow_Colse = true;
                    this.Close();
                }
            };
            this.Invoke(action);
        }

        private void UpgradeForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (!Allow_Colse)
            {
                if (MessageBox.Show("升级尚未完成，是否继续退出？", "提示", MessageBoxButtons.OKCancel, MessageBoxIcon.Information, MessageBoxDefaultButton.Button2) == DialogResult.Cancel)
                    e.Cancel = true;
            }
            else if (exception == null)
            {
                string path = System.IO.Path.Combine(Environment.CurrentDirectory, GlobalSetting.StartupApplication);
                if (!string.IsNullOrEmpty(GlobalSetting.StartupApplication) && System.IO.File.Exists(path))
                {
                    if (copy_fall_file_list.Count == 0)
                    {
                        if (e.CloseReason == CloseReason.UserClosing)
                            System.Diagnostics.Process.Start(path);
                    }
                    else
                        MessageBox.Show($"以下文件复制失败，请重新尝试:\r\n{string.Join("\r\n", copy_fall_file_list)}");
                    Application.Exit();
                }
            }
        }

        private void BackgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
        {
            // 用于解决在英文环境下,中文文件名乱码的问题
            System.Text.Encoding gbk = System.Text.Encoding.GetEncoding("gbk");
            ICSharpCode.SharpZipLib.Zip.ZipConstants.DefaultCodePage = gbk.CodePage;

            // 写入更新文件。
            bool write_xml = true;

            // 覆盖失败的文件
            // 删除不需要的文件。
            Action action = () =>
            {
                this.Label2.Text = "100%";
                this.progress1.Value = 1;
                Application.DoEvents();
                System.Threading.Thread.Sleep(100);
                this.Label1.Text = "正在安装...";
                Application.DoEvents();
            };
            this.Invoke(action);
            try
            {
                int po = 0;
                int sum = Download_List.Count;
                foreach (VersionInfo fileInfo in Download_List)
                {
                    po += 1;
                    action = () =>
                   {
                       if (fileInfo.Type.ToUpper() == "ZIP")
                           this.Label1.Text = string.Format("正在解压：{0}...", fileInfo.Name);
                       else
                           this.Label1.Text = string.Format("正在安装：{0}...", fileInfo.Name);
                       double value = po / (double)Download_List.Count;
                       this.Label2.Text = value == 1 ? "100%" : value.ToString("P");
                       this.progress1.Value = (int)value;
                       Application.DoEvents();
                   };
                    this.Invoke(action);
                    string sourceFileName = System.IO.Path.Combine(updaterDirectory, fileInfo.Name);
                    string destFileName = System.IO.Path.Combine(Environment.CurrentDirectory, fileInfo.Name);
                    if (System.IO.File.Exists(sourceFileName))
                    {
                        FileInfo info = new FileInfo(destFileName);
                        if (fileInfo.Type.ToUpper() == "ZIP")
                        {
                            ICSharpCode.SharpZipLib.Zip.FastZip FastZip = new ICSharpCode.SharpZipLib.Zip.FastZip();
                            FastZip.ExtractZip(sourceFileName, Environment.CurrentDirectory, "");
                        }
                        else
                            try
                            {
                                if (!System.IO.Directory.Exists(info.DirectoryName))
                                    System.IO.Directory.CreateDirectory(info.DirectoryName);
                                System.IO.File.Copy(sourceFileName, destFileName, true);
                            }
                            catch (Exception)
                            {
                                write_xml = false;
                                copy_fall_file_list.Add(sourceFileName, destFileName);
                            }
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "错误", MessageBoxButtons.OK);
                write_xml = false;
                this.Allow_Colse = true;
                this.Close();
            }

            if (write_xml)
            {
                string localConfigFileName = System.IO.Path.Combine(Environment.CurrentDirectory, GlobalSetting.LocalConfigFileName);
                System.IO.File.WriteAllText(localConfigFileName, GlobalSetting.ServerConfig.Xml);
            }

            foreach (VersionInfo Item in Delete_List)
            {
                try
                {
                    File.Delete(Path.Combine(Environment.CurrentDirectory, Item.Name));
                }
                catch (Exception)
                {
                }
            }
            action = () =>
           {
               this.Allow_Colse = true;
               this.Close();
           };
            this.Invoke(action);
        }

        private void PictureBox1_Paint(object sender, PaintEventArgs e)
        {
            e.Graphics.DrawString(this.Text, Title_Font, Brushes.White, 6, 6);
            closeRectangle = new Rectangle(this.Width - 32, 0, 32, 32);
            System.Drawing.Point p = this.PointToScreen(new Point(0, 0));
            p = new Point(MousePosition.X - p.X, MousePosition.Y - p.Y);
            Image Right_Img = Properties.Resources.defalutCloseButton;
            if (closeRectangle.Contains(p))
                Right_Img = Properties.Resources.FouseCloaseButton;
            e.Graphics.DrawImage(Right_Img, closeRectangle);
        }

        private void PictureBox1_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                int x = this.Location.X + e.X - DownPoint.X;
                int y = this.Location.Y + e.Y - DownPoint.Y;
                this.Location = new Point(x, y);
            }
            else
            {
                this.PictureBox1.Invalidate();
                if (closeRectangle != null && closeRectangle.Contains(e.Location))
                    this.Cursor = Cursors.Hand;
                else
                    this.Cursor = Cursors.Default;
            }
        }

        private void PictureBox1_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                Point p = this.PointToScreen(new Point(0, 0));
                p = new Point(MousePosition.X - p.X, MousePosition.Y - p.Y);
                if (closeRectangle.Contains(p))
                {
                    try
                    {
                        this.Close();
                    }
                    catch (Exception)
                    {
                    }
                }
            }
            DownPoint = new Point(e.X, e.Y);
        }

        private void PictureBox1_MouseLeave(object sender, EventArgs e)
        {
            this.PictureBox1.Invalidate();
        }

        private void PictureBox1_MouseUp(object sender, MouseEventArgs e)
        {
            if (this.Top < 0)
                this.Top = 0;
        }
    }
}
